package com.ai_daily.aitiantian;

import android.Manifest;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.DialogInterface;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.Paint;
import android.graphics.drawable.AnimationDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.os.Handler;
import android.os.Message;
import android.text.Layout;
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.TextView;

import androidx.appcompat.app.AlertDialog;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import com.ai_daily.aitiantian.api.ApiServiceHelper;
import com.ai_daily.aitiantian.base.BaseActivity;
import com.ai_daily.aitiantian.bean.CommonConfigResponseBean;
import com.ai_daily.aitiantian.bean.InitResponseBean;
import com.ai_daily.aitiantian.bean.param.ChatParam;
import com.ai_daily.aitiantian.bean.param.CloseChatParam;
import com.ai_daily.aitiantian.bean.param.CommonConfigParam;
import com.ai_daily.aitiantian.bean.param.DeviceInfoParam;
import com.ai_daily.aitiantian.bean.param.QueryInfoParam;
import com.ai_daily.aitiantian.bean.param.QueryLogoParam;
import com.ai_daily.aitiantian.control.InitConfig;
import com.ai_daily.aitiantian.control.NonBlockSyntherizer;
import com.ai_daily.aitiantian.core.inputstream.InFileStream;
import com.ai_daily.aitiantian.core.mini.AutoCheck;
import com.ai_daily.aitiantian.core.recog.IStatus;
import com.ai_daily.aitiantian.core.recog.MyRecognizer;
import com.ai_daily.aitiantian.core.recog.listener.IRecogListener;
import com.ai_daily.aitiantian.core.recog.listener.MessageStatusRecogListener;
import com.ai_daily.aitiantian.core.recog.listener.TimeRecorder;
import com.ai_daily.aitiantian.core.util.AuthUtil;
import com.ai_daily.aitiantian.core.util.MyLogger;
import com.ai_daily.aitiantian.core.wakeup.MyWakeup;
import com.ai_daily.aitiantian.core.wakeup.listener.IWakeupListener;
import com.ai_daily.aitiantian.core.wakeup.listener.RecogWakeupListener;
import com.ai_daily.aitiantian.listener.UiMessageListener;
import com.ai_daily.aitiantian.util.AppUtils;
import com.ai_daily.aitiantian.util.DeviceIdUtil;
import com.ai_daily.aitiantian.util.DrawableUtil;
import com.ai_daily.aitiantian.util.ExpressionConst;
import com.ai_daily.aitiantian.util.IOfflineResourceConst;
import com.ai_daily.aitiantian.util.IPAddressUtil;
import com.ai_daily.aitiantian.util.OfflineResource;
import com.ai_daily.aitiantian.util.StageType;
import com.ai_daily.aitiantian.util.StringProcessor;
import com.ai_daily.aitiantian.util.StringUtils;
import com.ai_daily.aitiantian.wakeup.MainHandlerConstant;
import com.baidu.speech.asr.SpeechConstant;
import com.baidu.tts.chainofresponsibility.logger.LoggerProxy;
import com.baidu.tts.client.SpeechSynthesizer;
import com.baidu.tts.client.SpeechSynthesizerListener;
import com.baidu.tts.client.SynthesizerTool;
import com.baidu.tts.client.TtsMode;
import com.bumptech.glide.Glide;
import com.king.zxing.util.CodeUtils;
import com.tencent.bugly.crashreport.CrashReport;
import com.tencent.vasdolly.helper.ChannelReaderUtil;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


/**
 * Created by huishuai on 2024/3/26.
 */

public class ActivityCommonUI extends BaseActivity implements IStatus, MainHandlerConstant {

    private boolean isActive = true;
    protected boolean isWakeUp = true;
    protected boolean isRecognizer = true;
    protected boolean isSynthesis = true;
    protected boolean isUpSpeaker = true;
    private String mSpeaker = "4";
    private String mSpeakSpeed = "5";
    protected boolean isSpeaking = false;
    protected ImageView imgExpression;
    private AnimationDrawable mExpressionDrawable;
    private StageType nextType = StageType.NONE;

    private TextView tvDeviceID;
    protected TextView txtResult;
    private ScrollView scrollView;
    private TextView txtQRInfo;
    private LinearLayout ll_qrcode, ll_qrcode_content;
    private ImageView iv_qrcode, iv_logo, iv_bind_qrcode;

    protected Handler handler;
    private boolean isLoopWakeup = true; //在唤醒状态下
    protected MyWakeup myWakeup;//语音唤醒控制器
    protected MyRecognizer myRecognizer;//语音识别控制器 MyRecognizer控制识别的流程
    private int backTrackInMs = -1;//1500 语音唤醒后回溯时间 小于0不启用

    private CountDownTimer mCountDownTimer = null;
    private boolean isContinueToWaitForQuestions = false; //是否继续等待问题
    private final int WAITING_TIME = 120000; //等待问题时长，超过关闭对话
    private CountDownTimer mListeningSpeekFinishDownTimer = null;

    private boolean isSpeakingHintEndMessage = false; //是否是提示结束

    //语音合成参数
    protected TtsMode ttsMode = IOfflineResourceConst.DEFAULT_SDK_TTS_MODE;
    protected boolean isOnlineSDK = TtsMode.ONLINE.equals(IOfflineResourceConst.DEFAULT_SDK_TTS_MODE);
    // 离线发音选择，VOICE_FEMALE即为离线女声发音。
    // assets目录下bd_etts_common_speech_m15_mand_eng_high_am-mix_vXXXXXXX.dat为离线男声模型文件；
    // assets目录下bd_etts_common_speech_f7_mand_eng_high_am-mix_vXXXXX.dat为离线女声模型文件;
    // assets目录下bd_etts_common_speech_yyjw_mand_eng_high_am-mix_vXXXXX.dat 为度逍遥模型文件;
    // assets目录下bd_etts_common_speech_as_mand_eng_high_am_vXXXX.dat 为度丫丫模型文件;
    // 在线合成sdk下面的参数不生效
    protected String offlineVoice = OfflineResource.VOICE_MALE;

    // ===============初始化参数设置完毕，更多合成参数请至getParams()方法中设置 =================

    // 主控制类，所有合成控制方法从这个类开始
    protected NonBlockSyntherizer synthesizer;
    private ApiServiceHelper apiServiceHelper;//网络请求帮助类
    private static final String TAG = "ActivityCommonUI";

    private int expressionState;//图片播放状态

    private int mCurrentSpeakLineNum;

    /**
     * 服务器返回的设备ID
     */
    private String deviceIdServer;

    @SuppressLint("HandlerLeak")
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        InFileStream.setContext(this);
        setContentView(R.layout.commonui);
        String channel = ChannelReaderUtil.getChannel(getApplicationContext());
        if ("primary".equals(channel)) {
            mSpeaker = "4";
            mSpeakSpeed = "5";
        } else if ("middle".equals(channel)) {
            mSpeaker = "4226";
            mSpeakSpeed = "7";
        } else {
            mSpeaker = "4";
            mSpeakSpeed = "5";
        }
        apiServiceHelper = new ApiServiceHelper();
        initView();
        handler = new Handler() {

            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                handleMsg(msg);
            }

        };
        setupTimer(WAITING_TIME);
        changeExpression(ExpressionConst.EXPRESSION_WAIT);
        //setStrictMode();
        //他的log管理
        MyLogger.setHandler(handler);
        initPermission();
    }

    private void setupTimer(long millisInFuture) {
        mCountDownTimer = new CountDownTimer(millisInFuture, 1000) {
            @Override
            public void onTick(long l) {
                MyLogger.info(TAG, "等待倒计时" + (l / 1000) + "秒");
                int secondsRemaining = (int) (l / 1000);
                if (secondsRemaining == 15) {//还有10秒结束时，提醒
                    speakEndHintText(Constants.DO_YOU_HAVE_ANY_QUESTIONS_TO_COMMUNICATE);
                }
            }

            @Override
            public void onFinish() {
                //关闭对话
                endOfConversation();
            }
        };
    }

    private void resetListeningSpeakFinishDownTimer(long millisInFuture) {
        if (mListeningSpeekFinishDownTimer == null) {
            mListeningSpeekFinishDownTimer = new CountDownTimer(millisInFuture, 1000) {
                @Override
                public void onTick(long l) {
                }

                @Override
                public void onFinish() {
                    onSpeakFinish();
                }
            };
        } else {
            mListeningSpeekFinishDownTimer.cancel();
            mListeningSpeekFinishDownTimer.start();
        }
    }

    /**
     * 语音朗读结束
     */
    private void onSpeakFinish() {
        isSpeaking = false;
        if (!isSpeakingHintEndMessage && isContinueToWaitForQuestions) { //如果是提示结束，不打断倒计时
            resetTimer();
        }
        isSpeakingHintEndMessage = false;
        if (isContinueToWaitForQuestions) {
            startRecognizer();
        }
        startWakeUp();
    }


    /**
     * 结束对话
     */
    private void endOfConversation() {
        speakMap.clear();
        utteranceId = 0;
        isContinueToWaitForQuestions = false;
        cancelRecognizer();
        apiServiceHelper.postCloseChat(getCloseChatParam());
    }

    private void resetTimer() {
        isContinueToWaitForQuestions = true;
        mCountDownTimer.cancel(); // 取消当前倒计时
        mCountDownTimer.start(); // 重新开始倒计时
    }

    private void initBaidu(Context context) {
        if (isWakeUp) {
            //唤醒模块核心
            IWakeupListener listener = new RecogWakeupListener(handler);
            myWakeup = new MyWakeup(this, listener);
        }
        if (isRecognizer) {
            //语音识别核心
            IRecogListener recogListener = new MessageStatusRecogListener(handler);
            // 改为 SimpleWakeupListener 后，不依赖handler，但将不会在UI界面上显示
            myRecognizer = new MyRecognizer(this, recogListener);
        }
        if (isSynthesis) {
            //语音合成引擎
            initialTts(); // 初始化TTS引擎
            if (!isOnlineSDK) {
                Log.i("SynthActivity", "so version:" + SynthesizerTool.getEngineInfo());
            }
        }
//        DeviceInfoParam param = getDeviceInfoParam();
//        apiServiceHelper.postInit(handler, param);

    }

    private DeviceInfoParam getDeviceInfoParam() {
        DeviceInfoParam param = new DeviceInfoParam();
        param.setTimestamp(String.valueOf(System.currentTimeMillis()));
        param.setAndroidId(DeviceIdUtil.getAndroidId(this));
        param.setDeviceid(DeviceIdUtil.getAndroidId(this));
        param.setIp(IPAddressUtil.getIPAddress(this));
        param.setUuid(DeviceIdUtil.getDeviceUUID());
        try {
            param.sign();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return param;
    }

    private QueryInfoParam getQueryInfoParam() {
        QueryInfoParam param = new QueryInfoParam();
        param.setTimestamp(String.valueOf(System.currentTimeMillis()));
        param.setAndroidId(DeviceIdUtil.getAndroidId(this));
        try {
            param.sign();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return param;
    }

    private QueryLogoParam getQueryLogoParam() {
        QueryLogoParam param = new QueryLogoParam();
        param.setTimestamp(String.valueOf(System.currentTimeMillis()));
        param.setAndroidId(DeviceIdUtil.getAndroidId(this));
        try {
            param.sign();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return param;
    }

    private ChatParam getChatParam(String question) {
        ChatParam param = new ChatParam();
        param.setTimestamp(String.valueOf(System.currentTimeMillis()));
        param.setAndroidId(DeviceIdUtil.getAndroidId(this));
        param.setQuestion(question);
        try {
            param.sign();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return param;
    }

    private CommonConfigParam getCommonConfigParam() {
        CommonConfigParam param = new CommonConfigParam();
        param.setTimestamp(String.valueOf(System.currentTimeMillis()));
        param.setAndroidId(DeviceIdUtil.getAndroidId(this));
        try {
            param.sign();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return param;
    }

    private CloseChatParam getCloseChatParam() {
        CloseChatParam param = new CloseChatParam();
        param.setTimestamp(String.valueOf(System.currentTimeMillis()));
        param.setAndroidId(DeviceIdUtil.getAndroidId(this));
        try {
            param.sign();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return param;
    }

    private void startRecognizer() {
        if (!isActive) return;//设备激活判断
        Map<String, Object> params = AuthUtil.getParam();
        params.put(SpeechConstant.ACCEPT_AUDIO_VOLUME, false);
        params.put(SpeechConstant.VAD, SpeechConstant.VAD_DNN);
        // 使用1537中文模型。其它PID参数请看文档
        params.put(SpeechConstant.PID, 1537);
        if (backTrackInMs > 0) {
            // 方案1  唤醒词说完后，直接接句子，中间没有停顿。开启回溯，连同唤醒词一起整句识别。
            // System.currentTimeMillis() - backTrackInMs ,  表示识别从backTrackInMs毫秒前开始
            params.put(SpeechConstant.AUDIO_MILLS, System.currentTimeMillis() - backTrackInMs);
        }
        cancelRecognizer();
        myRecognizer.start(params);
    }

    private void cancelRecognizer() {
        if (myRecognizer != null) {
            myRecognizer.cancel();
        }
    }

    // 点击“开始识别”按钮
    // 基于DEMO唤醒词集成第2.1, 2.2 发送开始事件开始唤醒
    @SuppressLint("HandlerLeak")
    private void startWakeUp() {
        if (!isActive) return;//设备激活判断
        Map<String, Object> params = AuthUtil.getParam();
        params.put(SpeechConstant.WP_WORDS_FILE, "assets:///WakeUp.bin");
        // "assets:///WakeUp.bin" 表示WakeUp.bin文件定义在assets目录下
        // params.put(SpeechConstant.ACCEPT_AUDIO_DATA,true);
        // params.put(SpeechConstant.IN_FILE,"res:///com/baidu/android/voicedemo/wakeup.pcm");
        // params里 "assets:///WakeUp.bin" 表示WakeUp.bin文件定义在assets目录下
        (new AutoCheck(getApplicationContext(), new Handler() {
            public void handleMessage(Message msg) {
                if (msg.what == 100) {
                    AutoCheck autoCheck = (AutoCheck) msg.obj;
                    synchronized (autoCheck) {
                        String message = autoCheck.obtainErrorMessage(); // autoCheck.obtainAllMessage();
                        MyLogger.info("AutoCheckMessage", message);
                    }
                }
            }
        }, false)).checkAsr(params);
        myWakeup.start(params);
    }

    private void stopWakeUp() {
        if (myWakeup != null) {
            myWakeup.stop();
        }
    }

    List<Pair<String, String>> texts = null;

    protected void handleMsg(Message msg) {
        switch (msg.what) {
            case INIT_SUCCESS: // 处理初始化成功消息
                handleInitSuccess();
                break;
            case STATUS_WAKEUP_SUCCESS:  // 处理唤醒成功消息
                handleWakeupSuccess();
                apiServiceHelper.cancelChat(); //取消之前的请求，防止回答以前的问题
                break;
            case STATUS_SYNTHESIS_SUCCESS:
//                SpeakNext(msg.arg1);
                break;
            case STATUS_ASR_END: // 用户说话结束
                //解决对话过程中，重新唤醒，无法识别用户问题（因为莫名奇妙调用了onAsrEnd，导致无法录音）
                break;
            case STATUS_ASR_EXIT: // 处理识别引擎空闲状态
                handleStatusNone();
                break;
            case STATUS_SPEECH_START:  // 处理语音开始状态
                handleSpeechStart();
                break;
            case STATUS_SPEECH_FINISH: // 处理语音成功状态,其实是一句话读完就回调(SDK 问题)
                handleSpeechEachSentenceFinish(msg);
                break;
            case STATUS_SYNTHESIS_ERROR:   // 处理语音合成错误
                handleSynthesisError();
                break;
            case STATUS_RECOGNIZER_SUCCESS:  // 处理识别成功消息
                MyLogger.info(TAG, "处理识别成功消息");
                // 记录语音识别结束时间
                TimeRecorder.setVoiceRecognitionEndTime(System.currentTimeMillis());
                handleRecognizerSuccess(msg);
                break;
            case STATUS_FINISHED:     // 处理状态结束消息
                handleStatusFinished();
                break;
            case STATUS_RECOGNIZER_ERROR:// 处理识别错误消息
                MyLogger.info(TAG, "处理识别错误消息");
                handleRecognizerError();
                break;
            case PRINT:
                print(msg);
                break;
            case REQUEST_INIT_SUCCESS_CODE:// 处理设备初始化成功消息
                handleInitSuccessCode(msg);
                break;
            case REQUEST_INIT_FAILURE_CODE://展示初始化接口调用失败弹窗，点击重启页面
                showInitFailureDialog();
                break;
            case STATUS_PLAY_RESULT:// 播放接口返回的答案
                handlePlayResult(msg);
                break;
            case STATUS_QUERY_INFO:
                apiServiceHelper.postQueryInfo(handler, getQueryInfoParam());
                break;
            case STATUS_QUERY_COMMON_CONFIG:// 处理设备配置信息查询消息
                handleQueryCommonConfig();
                break;
            case STATUS_SHOW_EXPIRE://设备到期
                deviceExpiration(msg);
                break;
            case STATUS_SHOW_NO_EXPIRE:// 处理设备未过期消息
                handleShowNoExpire();
                break;
            case STATUS_UPDATE_LOGO:// 处理更新Logo消息
                handleUpdateLogo(msg);
                break;
            case STATUS_SPEEK_PROGRESS://语音播放时文字选中效果
                //由于sdk没有说话结束回调，需要在这里定义一个监听，如果进度不走了，1秒说明结束了
                resetListeningSpeakFinishDownTimer(350);
                displayWordForWord(msg);
                break;
            case WHAT_MESSAGE_STATUS:
                //txtResult.setText(Constants.I_AM_THINKING_TEXT);
                break;
            default:
                break;
        }
    }

    // 处理初始化成功消息
    private void handleInitSuccess() {
        //语音合成引擎初始化成功，开启唤醒逻辑
        changeExpression(ExpressionConst.EXPRESSION_SPEAK);
        apiServiceHelper.postQueryInfo(handler, getQueryInfoParam());
        apiServiceHelper.postQueryLogo(handler, getQueryLogoParam());
        SpeakSystemText(Constants.DEFAULT_READY_TEXT);
        txtResult.setText(Constants.DEFAULT_READY_TEXT);
        stopWakeUp();//先关闭唤醒，防止自己把自己唤醒
        startRecognizer();
    }

    // 处理唤醒成功消息
    private void handleWakeupSuccess() {
        txtResult.setText(Constants.DEFAULT_IM_HERE_TEXT);
        stopSynthesizer();
        texts = null;
        SpeakSystemText(Constants.DEFAULT_IM_HERE_TEXT);
        nextType = StageType.START_RECOGNIZER;
        resetTimer();
        //获取敏感词、结束词
        apiServiceHelper.getKeywords(1);
        apiServiceHelper.getKeywords(2);
    }

    // 处理识别引擎空闲状态,为了连续对话，需要重新开启识别
    private void handleStatusNone() {
        MyLogger.info(TAG, "识别引擎空闲状态");
        if (!isSpeaking && isContinueToWaitForQuestions) {
            startRecognizer();
        }
    }

    // 处理语音开始状态
    private void handleSpeechStart() {
        isSpeaking = true;
        cancelRecognizer(); //解决在播放时，会识别自己的语音，无限对话问题
        changeExpression(ExpressionConst.EXPRESSION_SPEAK);
    }

    // 每句话结束都回调
    private void handleSpeechEachSentenceFinish(Message msg) {
        if (texts == null) {
            changeExpression(ExpressionConst.EXPRESSION_WAIT);
            Log.d(TAG, "EXPRESSION_WAIT");
        }
    }

    // 处理语音合成错误
    private void handleSynthesisError() {
        // 添加具体的错误处理逻辑
    }

    // 处理识别成功消息
    private void handleRecognizerSuccess(Message msg) {
        txtResult.setText(msg.obj.toString());
        mCountDownTimer.cancel();
        String hitSleepWord = StringUtils.containsAny(msg.obj.toString(), KeywordRepository.getInstance().getSleepWords());
        String hitSensitiveWord = StringUtils.containsAny(msg.obj.toString(), KeywordRepository.getInstance().getSensitiveWords());
        //有敏感词
        if (hitSensitiveWord != null) {
            takeAPhotoAndUploadIt(msg.obj.toString(), hitSensitiveWord);
        }
        //有休眠词
        if (hitSleepWord != null) {
            endOfConversation();
        } else {
            apiServiceHelper.postChatV3(handler, getChatParam(msg.obj.toString()));
        }
    }

    // 处理状态结束消息
    private void handleStatusFinished() {
//        if (!isLoopWakeup) {
//            startWakeUp();
//        }
    }

    // 处理识别错误消息
    private void handleRecognizerError() {
        // 添加具体的错误处理逻辑
    }

    // 处理设备初始化成功消息
    private void handleInitSuccessCode(Message msg) {
        if (msg.obj instanceof InitResponseBean) {
            InitResponseBean bean = (InitResponseBean) msg.obj;
            if (!TextUtils.isEmpty(bean.getDeviceid())) {
                deviceIdServer = bean.getDeviceid();
                tvDeviceID.setText(String.format("设备ID:%s", deviceIdServer));
            }
        }
        initBaidu(this);
    }

    // 播放
    private void handlePlayResult(Message msg) {
        String text = msg.obj.toString();
        SpeakText(text);
    }

    // 处理设备配置信息查询消息
    private void handleQueryCommonConfig() {
        if (ll_qrcode.getVisibility() != View.VISIBLE) {
            apiServiceHelper.postQueryCommonConfig(handler, getCommonConfigParam());
        }
    }

    // 处理设备未过期消息
    private void handleShowNoExpire() {
        if (txtResult.getVisibility() != View.VISIBLE) {
            ll_qrcode.setVisibility(View.GONE);
            txtResult.setVisibility(View.VISIBLE);
            isActive = true;
            txtResult.setText(Constants.DEFAULT_READY_TEXT);
            SpeakSystemText(Constants.DEFAULT_READY_TEXT);
            startWakeUp();
        }
    }

    // 处理更新Logo消息
    private void handleUpdateLogo(Message msg) {
        if (msg.obj != null && !TextUtils.isEmpty(msg.obj.toString())) {
            Glide.with(this).load(msg.obj).into(iv_logo);
        }
    }


    /**
     * 拍照并上传
     *
     * @param question         问题
     * @param hitSensitiveWord
     */
    private void takeAPhotoAndUploadIt(String question, String hitSensitiveWord) {
        //启动相机，拍照保存本地
        CameraUtil.getCameraInstance().takePhoto(this, path -> {
            //上传图片
            apiServiceHelper.uploadImage(question, hitSensitiveWord, deviceIdServer, path);
        });
    }

    /**
     * 设备到期
     */
    private void deviceExpiration(Message msg) {
        changeExpression(ExpressionConst.EXPRESSION_WAIT);
        ll_qrcode.setVisibility(View.VISIBLE);
        txtResult.setVisibility(View.INVISIBLE);
        isActive = false;
        stopWakeUp();
        cancelRecognizer();
        stopSynthesizer();
        if (msg.obj instanceof CommonConfigResponseBean) {
            CommonConfigResponseBean ccrb = (CommonConfigResponseBean) msg.obj;
            if (!TextUtils.isEmpty(ccrb.getQrCode())) {
                Glide.with(this)
                        .load(ccrb.getQrCode())
                        .into(iv_qrcode);
            }
            //绑定二维码
            Drawable logo = ContextCompat.getDrawable(this, R.mipmap.ic_launcher);
            Bitmap logoBitmap = DrawableUtil.drawableToBitmap(logo, 20);
            Bitmap bitmap = CodeUtils.createQRCode("http://tiantianai.ai-daily.com.cn/wx/index.html?deviceId=" + deviceIdServer,
                    400, logoBitmap);
            Glide.with(this)
                    .load(bitmap)
                    .into(iv_bind_qrcode);
            String info;
            if (!TextUtils.isEmpty(deviceIdServer)) {
                info = "设备ID:" + deviceIdServer + "\n" + ccrb.getDefaultExpireContent();
            } else {
                info = ccrb.getDefaultExpireContent();
            }

            txtQRInfo.setText(info);
        } else {
            txtQRInfo.setText("请求信息返回异常 " + msg.obj.toString());
        }
    }

    /**
     * 逐字显示回答文字，只显示最后一行
     *
     * @param msg
     */
    private void displayWordForWord(Message msg) {
        int progress = msg.arg1;
        int utteranceId = msg.arg2;
        String textNow = speakMap.get(String.valueOf(utteranceId));
        if (textNow == null) {
            return;
        }
        if (progress > textNow.length()) {//防止语音播报和文字对应不上时出现越界
            progress = textNow.length() - 1;
        }
        // 逐字显示
        String currentText = textNow.substring(0, progress);
        txtResult.setText(currentText);
        Log.d(TAG, "当前显示字幕： = " + currentText);

        // 获取TextView的Layout
        Layout layout = txtResult.getLayout();
        if (layout != null) {
            // 获取最后一行的行号
            int lineCount = layout.getLineCount();
            int lastLineIndex = lineCount - 1;

            // 获取最后一行的开始和结束位置
            int start = layout.getLineStart(lastLineIndex);
            int end = layout.getLineEnd(lastLineIndex);

            // 截取并显示最后一行
            String lastLineText = currentText.substring(start, end);
            txtResult.setText(lastLineText);
        }
    }

    /**
     * 获取行高的更准确方法
     *
     * @param textView 要获取行高的TextView
     * @return 行高（像素）
     */
    public int getAccurateLineHeight(TextView textView) {
        Layout layout = textView.getLayout();
        if (layout != null) {
            // 获取第一行的高度作为行高
            return layout.getLineBottom(0) - layout.getLineTop(0);
        } else {
            // 默认的行高计算方法
            Paint.FontMetricsInt fm = textView.getPaint().getFontMetricsInt();
            return fm.descent - fm.ascent;
        }
    }

    /**
     * 展示初始化接口调用失败弹窗，点击重启页面
     */
    private void showInitFailureDialog() {
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setCancelable(false);
        builder.setMessage(getString(R.string.text_system_init_failure));
        builder.setPositiveButton(R.string.text_ok, (DialogInterface.OnClickListener)
                (dialog, which) -> {
                    AppUtils.restartApp(ActivityCommonUI.this, true);
                });
        AlertDialog dialog = builder.create();
        dialog.show();
    }

    protected void toPrint(String str) {
        Message msg = Message.obtain();
        msg.obj = str;
        handler.sendMessage(msg);
    }

    private void print(Message msg) {
        String message = (String) msg.obj;
        if (TextUtils.isEmpty(message)) {
            MyLogger.info(message);
        }
    }

    protected void initView() {
        txtResult = findViewById(R.id.txtResulta);
        imgExpression = findViewById(R.id.img_expression);
        TextView tvVersion = findViewById(R.id.text_version);
        tvDeviceID = findViewById(R.id.text_device_id);

//        scrollView = findViewById(R.id.scrollView);
        ll_qrcode = findViewById(R.id.ll_qrcode);
        ll_qrcode_content = findViewById(R.id.ll_qrcode_content);
        ll_qrcode.setVisibility(View.GONE);
        txtQRInfo = findViewById(R.id.txt_activeinfo);
        iv_qrcode = findViewById(R.id.iv_qrcode);
        iv_bind_qrcode = findViewById(R.id.iv_bind_qrcode);
        iv_logo = findViewById(R.id.iv_logo);

        imgExpression.setImageResource(R.drawable.expression_wait);

        ll_qrcode_content.setOnClickListener(view -> apiServiceHelper.postQueryInfo(handler, getQueryInfoParam()));

        mExpressionDrawable = (AnimationDrawable) imgExpression.getDrawable();
        if (mExpressionDrawable != null) {
            mExpressionDrawable.start();
        }
        tvVersion.setText(String.format("V%s", AppUtils.getVersion(this)));
    }

    private void changeExpression(int state) {
        if (mExpressionDrawable != null && mExpressionDrawable.isRunning()) {
            mExpressionDrawable.stop();
            mExpressionDrawable = null;
        }

        int resId;
        switch (state) {

            case ExpressionConst.EXPRESSION_SPEAK:
                resId = R.drawable.expression_speak;
                break;

            case ExpressionConst.EXPRESSION_WAIT:
                resId = R.drawable.expression_wait;
                break;

            default:
                resId = R.drawable.expression_normal;
                break;
        }
        imgExpression.setImageResource(resId);
        mExpressionDrawable = (AnimationDrawable) imgExpression.getDrawable();
        if (mExpressionDrawable != null) {
            mExpressionDrawable.start();
        }
    }

    private void speakEndHintText(String text) {
        SpeakSystemText(text);
        isSpeakingHintEndMessage = true;
    }

    private int utteranceId = 0; //这次朗读的第几次合成
    HashMap<String, String> speakMap = new HashMap<>();
    private final StringBuilder textBuffer = new StringBuilder();

    private void SpeakText(String text) {
        if (!isActive) return;
        stringProcessor.setLengths(null);

        // 将新文本附加到缓冲区
        textBuffer.append(text);

        // 定义用于分割句子的标点符号
        String[] punctuationMarks = { "，", "。", "？", "！","～","~"};

        // 根据标点符号将缓冲区分割���句子
        List<String> sentences = new ArrayList<>();
        StringBuilder sentence = new StringBuilder();
        for (char c : textBuffer.toString().toCharArray()) {
            sentence.append(c);
            for (String mark : punctuationMarks) {
                if (String.valueOf(c).equals(mark)) {
                    sentences.add(sentence.toString());
                    sentence.setLength(0);
                    break;
                }
            }
        }

        // 清空缓冲区
        textBuffer.setLength(0);

        // 如果最后一个字符不是标点符号，将最后一个句子存回缓冲区
        if (sentence.length() > 0) {
            textBuffer.append(sentence);
        }

        // 朗读每个句子
        for (String s : sentences) {
            speak(s, ++utteranceId);
            speakMap.put(String.valueOf(utteranceId), s);
            Log.d(TAG, "处理后的句子: "+s);
        }
    }
    private void SpeakSystemText(String text) {
        if (!isActive) return;
        stringProcessor.setLengths(null);
        if (text.length() > 59) {
            //如果文字长度大于59，分多次播放
            if (texts == null) {
                texts = new ArrayList<>();
            }
            List<String> strings = stringProcessor.processString(text);
            for (int i = 0; i < strings.size(); i++) {
                speak(strings.get(i), ++utteranceId);
                speakMap.put(String.valueOf(utteranceId), strings.get(i));
            }
        } else {
            speak(text, ++utteranceId);
            speakMap.put(String.valueOf(utteranceId), text);
        }
    }

    private StringProcessor stringProcessor = new StringProcessor(59);

    //阶段播放 UID 播放阶段ID
    private void speak(String text, int utteranceId) {
        int result = synthesizer.speak(text, String.valueOf(utteranceId));
        checkResult(result, "speak");
    }

    /*
     * 停止合成引擎。即停止播放，合成，清空内部合成队列。
     */
    private void stopSynthesizer() {
        int result = synthesizer.stop();
        checkResult(result, "stop");
    }

    private void checkResult(int result, String method) {
        if (result != 0) {
            Message msg = Message.obtain();
            msg.obj = method;
            msg.what = STATUS_SYNTHESIS_ERROR;
            handler.sendMessage(msg);
            toPrint("error code :" + result + " method:" + method);
        }

    }


    protected void stopRecognizer() {
        myRecognizer.stop();
    }


    @Override
    protected void onDestroy() {
        // 基于DEMO唤醒词集成第5 退出事件管理器
        if (myWakeup != null) {
            myWakeup.release();
        }
        if (myRecognizer != null) {
            myRecognizer.release();
        }
        if (synthesizer != null) {
            synthesizer.release();
        }
        Log.i(TAG, "释放资源成功");
        super.onDestroy();
    }

    /**
     * android 6.0 以上需要动态申请权限
     */
    private void initPermission() {
        String[] permissions = {
                Manifest.permission.RECORD_AUDIO,
                Manifest.permission.ACCESS_NETWORK_STATE,
                Manifest.permission.INTERNET,
                Manifest.permission.WRITE_EXTERNAL_STORAGE,
                Manifest.permission.MODIFY_AUDIO_SETTINGS,
                Manifest.permission.ACCESS_WIFI_STATE,
                Manifest.permission.CHANGE_WIFI_STATE,
                Manifest.permission.READ_CONTACTS
                /* 下面是蓝牙用的，可以不申请
                Manifest.permission.BROADCAST_STICKY,
                Manifest.permission.BLUETOOTH,
                Manifest.permission.BLUETOOTH_ADMIN
                */

        };

        ArrayList<String> toApplyList = new ArrayList<>();

        for (String perm : permissions) {
            if (PackageManager.PERMISSION_GRANTED != ContextCompat.checkSelfPermission(this, perm)) {
                toApplyList.add(perm);
                // 进入到这里代表没有权限.

            }
        }
        String[] tmpList = new String[toApplyList.size()];
        if (!toApplyList.isEmpty()) {
            ActivityCompat.requestPermissions(this, toApplyList.toArray(tmpList), 123);
        } else {
            // TODO: 2024/3/30 请求 init接口
            requestInit();
//            initBaidu(this);
        }

    }

    private void requestInit() {
        DeviceInfoParam param = getDeviceInfoParam();
        apiServiceHelper.postInit(handler, param);
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        // 此处为android 6.0以上动态授权的回调，用户自行实现。
        if (requestCode == 123) {
            if (grantResults.length > 0
                    && grantResults[0] == PackageManager.PERMISSION_GRANTED) {

                requestInit();
            }
        }

    }

    /**
     * 初始化引擎，需要的参数均在InitConfig类里
     * <p>
     * DEMO中提供了3个SpeechSynthesizerListener的实现
     * MessageListener 仅仅用log.i记录日志，在logcat中可以看见
     * UiMessageListener 在MessageListener的基础上，对handler发送消息，实现UI的文字更新
     * FileSaveListener 在UiMessageListener的基础上，使用 onSynthesizeDataArrived回调，获取音频流
     */
    protected void initialTts() {
        LoggerProxy.printable(true); // 日志打印在logcat中
        // 设置初始化参数
        // 此处可以改为 含有您业务逻辑的SpeechSynthesizerListener的实现类
        SpeechSynthesizerListener listener = new UiMessageListener(handler);
        InitConfig config = getInitConfig(listener);
        synthesizer = new NonBlockSyntherizer(this, config, handler); // 此处可以改为MySyntherizer 了解调用过程
    }


    /**
     * 合成的参数，可以初始化时填写，也可以在合成前设置。
     *
     * @return 合成参数Map
     */
    protected Map<String, String> getParams() {
        Map<String, String> params = new HashMap<>();
        // 以下参数均为选填
        // 设置在线发声音人： 0 普通女声（默认） 1 普通男声 3 情感男声<度逍遥> 4 情感儿童声<度丫丫>, 其它发音人见文档
        //4226 南方女主播
        params.put(SpeechSynthesizer.PARAM_SPEAKER, mSpeaker);
        // 设置合成的音量，0-15 ，默认 5
        params.put(SpeechSynthesizer.PARAM_VOLUME, "15");
        // 设置合成的语速，0-15 ，默认 5
        params.put(SpeechSynthesizer.PARAM_SPEED, mSpeakSpeed);
        // 设置合成的语调，0-15 ，默认 5
        params.put(SpeechSynthesizer.PARAM_PITCH, "5");
        if (!isOnlineSDK) {
            // 在线SDK版本没有此参数。

            /*
            params.put(SpeechSynthesizer.PARAM_MIX_MODE, SpeechSynthesizer.MIX_MODE_DEFAULT);
            // 该参数设置为TtsMode.MIX生效。即纯在线模式不生效。
            // MIX_MODE_DEFAULT 默认 ，wifi状态下使用在线，非wifi离线。在线状态下，请求超时6s自动转离线
            // MIX_MODE_HIGH_SPEED_SYNTHESIZE_WIFI wifi状态下使用在线，非wifi离线。在线状态下， 请求超时1.2s自动转离线
            // MIX_MODE_HIGH_SPEED_NETWORK ， 3G 4G wifi状态下使用在线，其它状态离线。在线状态下，请求超时1.2s自动转离线
            // MIX_MODE_HIGH_SPEED_SYNTHESIZE, 2G 3G 4G wifi状态下使用在线，其它状态离线。在线状态下，请求超时1.2s自动转离线
            // params.put(SpeechSynthesizer.PARAM_MIX_MODE_TIMEOUT, SpeechSynthesizer.PARAM_MIX_TIMEOUT_TWO_SECOND);
            // 离在线模式，强制在线优先。在线请求后超时2秒后，转为离线合成。
            */
            // 离线资源文件， 从assets目录中复制到临时目录，需要在initTTs方法前完成
            OfflineResource offlineResource = createOfflineResource(offlineVoice);
            // 声学模型文件路径 (离线引擎使用), 请确认下面两个文件存在
            params.put(SpeechSynthesizer.PARAM_TTS_TEXT_MODEL_FILE, offlineResource.getTextFilename());
            params.put(SpeechSynthesizer.PARAM_TTS_SPEECH_MODEL_FILE, offlineResource.getModelFilename());
        }
        return params;
    }

    @SuppressLint("HandlerLeak")
    protected InitConfig getInitConfig(SpeechSynthesizerListener listener) {
        Map<String, String> params = getParams();
        // 添加你自己的参数
        InitConfig initConfig;
        // appId appKey secretKey 网站上您申请的应用获取。注意使用离线合成功能的话，需要应用中填写您app的包名。包名在build.gradle中获取。
        initConfig = new InitConfig(AuthUtil.getAppId(), AuthUtil.getAk(), AuthUtil.getSk(), AuthUtil.getSN(), ttsMode, params, listener);
        // 如果您集成中出错，请将下面一段代码放在和demo中相同的位置，并复制InitConfig 和 AutoCheck到您的项目中
        // 上线时请删除AutoCheck的调用
        com.ai_daily.aitiantian.util.AutoCheck.getInstance(getApplicationContext()).check(initConfig, new Handler() {
            @Override
            public void handleMessage(Message msg) {
                if (msg.what == 100) {
                    com.ai_daily.aitiantian.util.AutoCheck autoCheck = (com.ai_daily.aitiantian.util.AutoCheck) msg.obj;
                    synchronized (autoCheck) {
                        String message = autoCheck.obtainDebugMessage();
                        toPrint(message); // 可以用下面一行替代，在logcat中查看代码
                        // Log.w("AutoCheckMessage", message);
                    }
                }
            }

        });
        return initConfig;
    }

    protected OfflineResource createOfflineResource(String voiceType) {
        OfflineResource offlineResource = null;
        try {
            offlineResource = new OfflineResource(this, voiceType);
        } catch (IOException e) {
            // IO 错误自行处理
            e.printStackTrace();
            toPrint("【error】:copy files from assets failed." + e.getMessage());
        }
        return offlineResource;
    }
}
